package com.sos.service.impl;

import com.sos.exception.CustomException;
import com.sos.exception.ErrorCode;
import com.sos.model.dto.*;
import com.sos.model.entity.HelperInfo;
import com.sos.model.entity.User;
import com.sos.model.vo.ResultVO;
import com.sos.repository.HelperInfoRepository;
import com.sos.repository.UserRepository;
import com.sos.service.FileUploadService;
import com.sos.service.MailService;
import com.sos.service.UserService;
import com.sos.service.VerifyCodeService;
import com.sos.util.JwtUtils;
import com.sos.util.UserContextUtils;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
    private final UserRepository userRepository;
    private final HelperInfoRepository helperInfoRepository;
    private final PasswordEncoder passwordEncoder;
    private final JwtUtils jwtUtils;
    private final RedisTemplate<String, Object> redisTemplate;
    private final MailService mailService;
    private final UserContextUtils userContextUtils;
    private final VerifyCodeService verifyCodeService;
    private final FileUploadService fileUploadService;
    
    // 在线状态缓存过期时间（秒）
    private static final long ONLINE_STATUS_EXPIRE_SECONDS = 30;

    @Override
    @Transactional
    public ResultVO<UserResponseDTO> register(UserRegisterDTO registerDTO) {
        // 检查用户名是否已存在
        if (userRepository.existsByUsername(registerDTO.getUsername())) {
            throw new CustomException(ErrorCode.USER_ALREADY_EXIST);
        }

        // 检查手机号是否已存在
        if (userRepository.existsByPhone(registerDTO.getPhone())) {
            throw new CustomException(ErrorCode.USER_ALREADY_EXIST);
        }

        // 检查邮箱是否已存在（如果提供了邮箱）
        if (registerDTO.getEmail() != null && !registerDTO.getEmail().isEmpty()
                && userRepository.existsByEmail(registerDTO.getEmail())) {
            throw new CustomException(ErrorCode.USER_ALREADY_EXIST);
        }

        // 创建用户对象
        User user = new User();
        user.setUsername(registerDTO.getUsername());
        // 加密密码
        user.setPassword(passwordEncoder.encode(registerDTO.getPassword()));
        user.setPhone(registerDTO.getPhone());
        user.setEmail(registerDTO.getEmail());
        user.setRealName(registerDTO.getRealName());
        user.setGender(registerDTO.getGender());
        // 设置默认头像
        user.setAvatar("/api/files/default-avatar.png");
        // 默认值：canHelp=1（愿意帮助），status=1（正常）

        // 保存用户
        User savedUser = userRepository.save(user);

        // 转换为响应DTO
        UserResponseDTO responseDTO = convertToResponseDTO(savedUser);

        return ResultVO.success(responseDTO);
    }

    @Override
    public ResultVO<LoginResponseDTO> login(UserLoginDTO loginDTO) {
        // 尝试通过用户名或手机号查找用户
        User user = userRepository.findByUsername(loginDTO.getUsernameOrPhone())
                .orElseGet(() -> userRepository.findByPhone(loginDTO.getUsernameOrPhone())
                        .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND)));

        // 检查用户是否被禁用
        if (user.getStatus() == 0) {
            throw new CustomException(ErrorCode.USER_DISABLED);
        }

        // 检查密码
        if (!passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) {
            throw new CustomException(ErrorCode.PASSWORD_ERROR);
        }

        // 生成JWT令牌
        String token = jwtUtils.generateToken(user.getUsername());
        String refreshToken = jwtUtils.generateRefreshToken(user.getUsername());

        // 将用户信息存入Redis，设置过期时间与token一致
        redisTemplate.opsForValue().set(
                "sos:session:" + user.getUsername(),
                user,
                7200,
                TimeUnit.SECONDS
        );
        // 关键：将User实体转换为UserDetailDTO（只保留需要返回的字段）
        UserDetailDTO userDetailDTO = convertToUserDetailDTO(user);
        // 构建返回结果
        LoginResponseDTO responseDTO = new LoginResponseDTO();
        responseDTO.setToken(token);
        responseDTO.setRefreshToken(refreshToken);
        // 此处返回UserResponseDTO对象
        responseDTO.setUserDetailDTO(userDetailDTO); // 设置转换后的DTO

        return ResultVO.success(responseDTO);
    }

    @Override
    public void logout(String token) {
        // 1. 校验Token是否有效（避免对无效Token执行操作）
        if (!jwtUtils.isTokenValid(token)) {
            throw new RuntimeException("Token已失效或不存在");  // 会被全局异常处理器捕获
        }
        // 2. 计算Token剩余有效期（将Token加入黑名单，过期时间设为Token剩余存活时间）
        long expireTime = jwtUtils.getExpirationDateFromToken(token).getTime() - System.currentTimeMillis();
        if (expireTime > 0) {
            // 3. 将Token存入Redis黑名单（键：固定前缀+Token，值：任意，过期时间=Token剩余时间）
            String blacklistKey = "sos:blacklist:" + token;
            redisTemplate.opsForValue().set(blacklistKey, "logout", expireTime, TimeUnit.MILLISECONDS);
        }
    }
    
    @Override
    public ResultVO<LoginResponseDTO> refreshToken(RefreshTokenDTO refreshTokenDTO) {
        String refreshToken = refreshTokenDTO.getRefreshToken();
        
        // 验证刷新令牌是否有效
        if (!jwtUtils.isTokenValid(refreshToken)) {
            throw new CustomException(ErrorCode.NOT_LOGIN, "刷新令牌无效或已过期");
        }
        
        // 从刷新令牌中提取用户名
        String username = jwtUtils.getUsernameFromToken(refreshToken);
        
        // 查找用户
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
        
        // 检查用户是否被禁用
        if (user.getStatus() == 0) {
            throw new CustomException(ErrorCode.USER_DISABLED);
        }
        
        // 生成新的访问令牌和刷新令牌
        String newToken = jwtUtils.generateToken(username);
        String newRefreshToken = jwtUtils.generateRefreshToken(username);
        
        // 将用户信息存入Redis，设置过期时间与token一致
        redisTemplate.opsForValue().set(
                "sos:session:" + username,
                user,
                7200,
                TimeUnit.SECONDS
        );
        
        // 关键：将User实体转换为UserDetailDTO（只保留需要返回的字段）
        UserDetailDTO userDetailDTO = convertToUserDetailDTO(user);
        // 构建返回结果
        LoginResponseDTO responseDTO = new LoginResponseDTO();
        responseDTO.setToken(newToken);
        responseDTO.setRefreshToken(newRefreshToken);
        // 此处返回UserResponseDTO对象
        responseDTO.setUserDetailDTO(userDetailDTO); // 设置转换后的DTO
        
        return ResultVO.success(responseDTO);
    }

    @Override
    public User getCurrentUser() {
        // 1. 获取当前用户ID
        Long userId = userContextUtils.getCurrentUserId();
        
        // 2. 根据ID查找用户
        return userRepository.findById(userId)
                .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
    }
    
    @Override
    @Transactional
    public ResultVO<UserResponseDTO> updateUserInfo(UserUpdateDTO userUpdateDTO) {
        // 1. 获取当前用户
        Long userId = userContextUtils.getCurrentUserId();
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

        // 2. 检查用户名是否已存在（如果用户尝试更改用户名）
        if (userUpdateDTO.getUsername() != null && !userUpdateDTO.getUsername().equals(user.getUsername())) {
            if (userRepository.existsByUsername(userUpdateDTO.getUsername())) {
                throw new CustomException(ErrorCode.USERNAME_ALREADY_EXIST);
            }
            user.setUsername(userUpdateDTO.getUsername());
        }
        
        // 3. 检查手机号是否已存在（如果用户尝试更改手机号）
        if (userUpdateDTO.getPhone() != null && !userUpdateDTO.getPhone().equals(user.getPhone())) {
            if (userRepository.existsByPhone(userUpdateDTO.getPhone())) {
                throw new CustomException(ErrorCode.PHONE_ALREADY_EXIST);
            }
            user.setPhone(userUpdateDTO.getPhone());
        }
        
        // 4. 检查邮箱是否已存在（如果用户尝试更改邮箱）
        if (userUpdateDTO.getEmail() != null && !userUpdateDTO.getEmail().equals(user.getEmail())) {
            if (userRepository.existsByEmail(userUpdateDTO.getEmail())) {
                throw new CustomException(ErrorCode.EMAIL_ALREADY_EXIST);
            }
            user.setEmail(userUpdateDTO.getEmail());
        }
        
        // 5. 更新用户信息（除了status字段）
        user.setRealName(userUpdateDTO.getRealName());
        user.setIdCard(userUpdateDTO.getIdCard());
        user.setGender(userUpdateDTO.getGender());
        user.setCanHelp(userUpdateDTO.getCanHelp());
        
        // 6. 保存更新
        User updatedUser = userRepository.save(user);
        
        // 7. 转换为响应DTO并返回
        UserResponseDTO responseDTO = convertToResponseDTO(updatedUser);
        return ResultVO.success(responseDTO);
    }
    
    @Override
    @Transactional
    public ResultVO<?> updatePassword(PasswordUpdateDTO passwordUpdateDTO) {
        // 1. 获取当前用户
        Long userId = userContextUtils.getCurrentUserId();
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
        
        // 2. 验证旧密码是否正确
        if (!passwordEncoder.matches(passwordUpdateDTO.getOldPassword(), user.getPassword())) {
            throw new CustomException(ErrorCode.PASSWORD_ERROR, "旧密码不正确");
        }
        
        // 3. 更新密码
        user.setPassword(passwordEncoder.encode(passwordUpdateDTO.getNewPassword()));
        
        // 4. 保存更新
        userRepository.save(user);
        
        return ResultVO.success("密码更新成功");
    }
    
    @Override
    @Transactional
    public ResultVO<?> updateHelperStatus(HelperStatusUpdateDTO helperStatusUpdateDTO) {
        // 1. 获取当前用户
        Long userId = userContextUtils.getCurrentUserId();
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
        
        // 2. 更新帮助他人状态
        user.setCanHelp(helperStatusUpdateDTO.getCanHelp());
        
        // 3. 保存更新
        User updatedUser = userRepository.save(user);
        
        return ResultVO.success();
    }
    
    @Override
    public ResultVO<UserDetailDTO> getCurrentUserDetail() {
        // 1. 获取当前用户
        Long userId = userContextUtils.getCurrentUserId();
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
        
        // 2. 转换为UserDetailDTO并返回
        UserDetailDTO userDetailDTO = convertToUserDetailDTO(user);
        return ResultVO.success(userDetailDTO);
    }
    
    @Override
    public ResultVO<UserHelperStatusDTO> getCurrentUserHelperStatus() {
        // 1. 获取当前用户
        Long userId = userContextUtils.getCurrentUserId();
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
        
        // 2. 创建UserHelperStatusDTO并设置canHelp字段
        UserHelperStatusDTO helperStatusDTO = new UserHelperStatusDTO();
        helperStatusDTO.setCanHelp(user.getCanHelp());
        
        // 3. 返回结果
        return ResultVO.success(helperStatusDTO);
    }
    
    @Override
    public void sendPasswordResetCode(PasswordResetCodeRequestDTO requestDTO) {
        String email = requestDTO.getEmail();
        
        // 获取当前用户
        User currentUser = getCurrentUser();

        // 检查该邮箱是否为当前用户的实际邮箱
        if (!email.equals(currentUser.getEmail())) {
            throw new CustomException(ErrorCode.USER_NOT_FOUND, "该邮箱不是当前用户的注册邮箱");
        }

        // 生成并发送验证码
        String code = verifyCodeService.generateCode();
        verifyCodeService.saveCode(email, code);
        mailService.sendVerificationCode(email, code);
    }

    @Override
    @Transactional
    public void resetPassword(VerifyCodeResetPasswordDTO resetDTO) {
        String email = resetDTO.getEmail();
        String code = resetDTO.getCode();
        String newPassword = resetDTO.getNewPassword();

        // 验证验证码
        verifyCodeService.validateCode(email, code);

        // 查找用户并更新密码
        User user = userRepository.findByEmail(email)
                .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

        user.setPassword(passwordEncoder.encode(newPassword));
        userRepository.save(user);
    }


    @Override
    public ResultVO<HelperInfo> getUserOnlineStatus() {
        // 1. 获取当前用户
        Long userId = userContextUtils.getCurrentUserId();
        log.info("获取用户 {} 的在线状态", userId);
        
        // 2. 先从Redis中查找
        String redisKey = "sos:online:status:" + userId;
        Map<Object, Object> statusInfo = redisTemplate.opsForHash().entries(redisKey);
        log.info("从Redis获取用户 {} 的状态信息: {}", userId, statusInfo);
        
        if (!statusInfo.isEmpty()) {
            // 从Redis中构建HelperInfo对象
            HelperInfo helperInfo = new HelperInfo();
            helperInfo.setUserId(userId);
            
            Object onlineStatus = statusInfo.get("onlineStatus");
            if (onlineStatus != null) {
                helperInfo.setOnlineStatus(Integer.parseInt(onlineStatus.toString()));
            }
            
            Object lastLatitude = statusInfo.get("lastLatitude");
            if (lastLatitude != null) {
                helperInfo.setLastLatitude(BigDecimal.valueOf(Double.parseDouble(lastLatitude.toString())));
            }
            
            Object lastLongitude = statusInfo.get("lastLongitude");
            if (lastLongitude != null) {
                helperInfo.setLastLongitude(BigDecimal.valueOf(Double.parseDouble(lastLongitude.toString())));
            }
            
            Object lastActiveTime = statusInfo.get("lastActiveTime");
            if (lastActiveTime != null) {
                // 这里简化处理，实际应用中可能需要更精确的时间转换
                helperInfo.setLastActiveTime(LocalDateTime.now());
            }
            
            log.info("从Redis获取到用户 {} 的在线状态: onlineStatus={}", userId, helperInfo.getOnlineStatus());
            return ResultVO.success(helperInfo);
        }
        
        // 3. Redis中没有则从数据库查找
        log.info("Redis中未找到用户 {} 的状态信息，从数据库查找", userId);
        HelperInfo helperInfo = helperInfoRepository.findByUserId(userId);
        if (helperInfo == null) {
            log.warn("数据库中未找到用户 {} 的帮助者信息", userId);
            throw new CustomException(ErrorCode.HELPER_INFO_NOT_FOUND);
        }
        
        log.info("从数据库获取到用户 {} 的在线状态: onlineStatus={}", userId, helperInfo.getOnlineStatus());
        return ResultVO.success(helperInfo);
    }

    /**
     * 将User实体转换为UserResponseDTO
     */
    private UserResponseDTO convertToResponseDTO(User user) {
        UserResponseDTO dto = new UserResponseDTO();
        dto.setId(user.getId());
        dto.setUsername(user.getUsername());
        dto.setPhone(user.getPhone());
        dto.setEmail(user.getEmail());
        dto.setRealName(user.getRealName());
        dto.setGender(user.getGender());
        dto.setCanHelp(user.getCanHelp());
        dto.setStatus(user.getStatus());
        // 如果用户没有头像，则使用默认头像
        dto.setAvatar(user.getAvatar() != null ? user.getAvatar() : "/api/files/default-avatar.png");
        dto.setCreatedTime(user.getCreatedTime());
        return dto;
    }

    /**
     * 将User实体转换为UserDetailDTO（控制返回字段）
     */
    private UserDetailDTO convertToUserDetailDTO(User user) {
        UserDetailDTO dto = new UserDetailDTO();
        dto.setId(user.getId());
        dto.setUsername(user.getUsername());
        dto.setPhone(user.getPhone());
        dto.setEmail(user.getEmail());
        dto.setRealName(user.getRealName());
        dto.setIdCard(user.getIdCard());
        dto.setGender(user.getGender());
        dto.setCanHelp(user.getCanHelp());
        dto.setStatus(user.getStatus());
        // 如果用户没有头像，则使用默认头像
        dto.setAvatar(user.getAvatar() != null ? user.getAvatar() : "/api/files/default-avatar.png");
        dto.setCreatedTime(user.getCreatedTime());
        dto.setUpdatedTime(user.getUpdatedTime());
        return dto;
    }
}